package net.htjs.juc.waitnotify;

import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * 保护性暂停模式
 * */
@Slf4j
public class Demo4 {

    public static void main(String[] args) throws InterruptedException {
        for(int i =0; i < 3; i++) {
            new People().start();
        }
        Thread.sleep(1000);
        for(Integer id : Mailboxes.getIds()) {
            new Postman(id, "mail:" + id).start();
        }
    }

    @Slf4j
    static class People extends Thread {
        @Override
        public void run() {
            GuardedObject go = Mailboxes.createGuardedObject();
            log.info("开始收信 id:{}", go.getId());
            Object mail = go.get(5000);
            log.info("收到结果 id:{}, response:{}", go.getId(), mail);
        }
    }

    @Slf4j
    static class Postman extends Thread {

        private int id;
        private String mail;

        public Postman(int id, String mail) {
            this.id = id;
            this.mail = mail;
        }

        @Override
        public void run() {
            GuardedObject go = Mailboxes.getGuardedObject(id);
            log.info("开始发信 id:{}", go.getId());
            go.complete(mail);
        }
    }


    static class Mailboxes {
        //使用线程安全的集合实现 hashtable
        private static Map<Integer, GuardedObject> boxes = new Hashtable<>();

        private static int id = 1;

        /**
         * 由于i++不是原子性的，这里要用synchronized修饰
         * @return
         */
        public static synchronized int generateId() {
            return id++;
        }

        /**
         * 获取guardedobject对象
         * @param id
         * @return
         * remove方法除了会移除键值还会返回键值内容
         */
        public static GuardedObject getGuardedObject(int id) {
            return boxes.remove(id);
        }

        /**
         * 获取guardedobject对象
         * @return
         */
        public static GuardedObject createGuardedObject() {
            GuardedObject go = new GuardedObject(generateId());
            boxes.put(go.getId(), go);
            return go;
        }

        public static Set<Integer> getIds() {
            return boxes.keySet();
        }
    }



    static class GuardedObject {
        //唯一标识GuardeObject
        private int id;
        private Object response;

        public GuardedObject(int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        /**
         * //获取结果
         * 表示最多等多久
         * @param timeout
         * @return
         */
        public Object get(long timeout) {
            synchronized (this) {
                //开始时间
                long begin = System.currentTimeMillis();
                //经历的时间
                long passedTime = 0;
                while(response == null) {
                    //这一轮循环应该等待时间
                    long waitTime = timeout - passedTime;
                    //经历的时间超过了最大等待时间，退出循环
                    if(waitTime <= 0) {
                        break;
                    }
                    try {
                        this.wait(waitTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //求得经历时间
                    passedTime = System.currentTimeMillis() - begin;
                }
            }
            return response;
        }

        public void complete(Object response) {
            synchronized (this) {
                //给结果成员变量赋值
                this.response = response;
                this.notifyAll();
            }
        }
    }

}
